除了 Dart 自带的 io 包之外,人气较高的是 dio 库,其内部也是由 HttpClient 发起请求的,只不过做了一些封装,让我们使用起来更方便一些,它支持 Restful APIFormData、拦截器、请求取消、Cookie 管理、文件上传/下载、超时、自定义适配器等。

引入并创建实例

dio 获取最新版本,它的 github :dio

dependencies:
  dio: ^3.0.9

然后在 dart 文件中添加依赖:

import 'package:dio/dio.dart';

接下来就是创建 dio 实例了:

Dio dio = Dio();

super easy~接下来就可以调用 dio 的一系列方法来进行我们的操作了:

get 请求

和 IO 包一样,dio 同样提供了两种方法来帮助我们完成 get 请求

get

  Future<Response<T>> get<T>(
    String path, {
    Map<String, dynamic> queryParameters,
    Options options,
    CancelToken cancelToken,
    ProgressCallback onReceiveProgress,
  })
  • path,请求路径,需要 http://https:// 开头,否则会抛异常。

  • queryParameters,请求参数

    dio.get("http://xx.xx.xx.xx:8080/xxxxx/SelectBillByUserId?userId=d969887a274347b98a0b99a830a2d6f");
    

    等效于:

    dio.get("http://xx.xx.xx.xx:8080/xxxxx/SelectBillByUserId,queryParameters: {
          "userId":"d969887a274347b98a0b99a830a2d6f",
        });
    
  • options,一个 Options 对象,用于描述 http 请求信息和配置。 每个 Dio 实例都有一个针对自己发出的所有请求的基本配置(在创建 Dio 对象时设置),当发出单个请求时,我们可以使用 Options 覆盖基本配置。

      Options({
        String method,    
        int sendTimeout,    
        int receiveTimeout,    
        Map<String, dynamic> extra,    
        Map<String, dynamic> headers,    
        ResponseType responseType,    
        String contentType,    
        ValidateStatus validateStatus,
        bool receiveDataWhenStatusError,
        bool followRedirects,
        int maxRedirects,
        RequestEncoder requestEncoder,
        ResponseDecoder responseDecoder,
      })
    
    • method,http 请求方法

    • sendTimeout,发送超时时间,单位是毫秒.

    • receiveTimeout,接收超时时间,单位是毫秒.

    • extra,用户自定义字段,可以在 InterceptorTransformerResponse 中取到.

    • headers,请求头

    • responseType,表示期望以那种格式(方式)接受响应数据。目前 ResponseType 接受三种类型 JSON, STREAM, PLAIN

      默认值是 JSON, 当响应头中 content-typeapplication/json 时,dio 会自动将响应内容转化为 json 对象。 如果想以二进制方式接受响应数据,如下载一个二进制文件,那么可以使用 STREAM. 如果想以文本(字符串)格式接收响应数据,请使用 PLAIN.

    • contentType,请求的 Content-Type。默认值为 application/json; charset=utf-8

    • validateStatus,决定 http 响应状态码是否被 dio 视为请求成功, 返回true , 请求结果就会按成功处理,否则会按失败处理.

    • receiveDataWhenStatusError,http 状态码失败时是否接收响应数据。

    • followRedirects,如果此请求应自动跟随重定向,则将此属性设置为 true 。 默认为 true 。

    • maxRedirects,将 followRedirects 属性设置为 true 时要遵循的最大重定向数。 如果超过此数字,则会添加一个错误事件,并带有一个 RedirectException。 默认值为 5。

    • requestEncoder,默认请求编码器是 utf8encoder,您可以通过此选项设置自定义编码器。

    • responseDecoder,默认的响应解码器是 utf8decoder,您可以通过此选项设置自定义解码器,它将在Transformer 中使用。

  • cancelToken,你可以通过 cancel token 来取消发起的请求:

    注意: 同一个cancel token 可以用于多个请求,当一个cancel token取消时,所有使用该cancel token的请求都会被取消。

  • onReceiveProgress,接收进度

getUri

  Future<Response<T>> getUri<T>(
    Uri uri, {
    Options options,
    CancelToken cancelToken,
    ProgressCallback onReceiveProgress,
  })

没什么好讲的,具体属性参见 get 方法。

post 请求

同样是两种方法:

post

  Future<Response<T>> post<T>(
    String path, {
    data,
    Map<String, dynamic> queryParameters,
    Options options,
    CancelToken cancelToken,
    ProgressCallback onSendProgress,
    ProgressCallback onReceiveProgress,
  })
  • path,请求路径
  • data,请求的数据
  • queryParameters,同 get
  • options,同 get
  • cancelToken,同 get
  • onSendProgress,发送数据进度
  • onReceiveProgress,同 get

postUri

  Future<Response<T>> postUri<T>(
    Uri uri, {
    data,
    Options options,
    CancelToken cancelToken,
    ProgressCallback onSendProgress,
    ProgressCallback onReceiveProgress,
  })

不赘述

下载数据

dio 还提供了一些其他的 Restful API,这些 API 都是 request 的别名。

Future get(...)

Future post(...)

Future put(...)

Future delete(...)

Future head(...)

Future put(...)

Future path(...)

Future download(...)

其他的不说了,download 来讲一下,dio 提供了下载请求:

  Future<Response> download(
    String urlPath,    //请求路径
    savePath, //保存路径
      {
    ProgressCallback onReceiveProgress,    //接收进度
    Map<String, dynamic> queryParameters,    //请求参数
    CancelToken cancelToken,    //取消请求 token
    bool deleteOnError = true,    //发生错误时是否删除
    String lengthHeader = Headers.contentLengthHeader,    //消息头长度
    data,    //请求的数据
    Options options,    //请求选项,参见 get 方法
  })

并发请求

dio 可以发起多个并发请求:

response = await Future.wait([dio.post("/info"), dio.get("/token")]);

响应

不管是 get 请求还是 post 请求,都会返回一个请求响应:

  Response({
    this.data,
    this.headers,
    this.request,
    this.isRedirect,
    this.statusCode,
    this.statusMessage,
    this.redirects,
    this.extra,
  })
  • data,响应数据,可能已经被转换了类型, 详情请参考 Options 中的 ResponseType.
  • headers,响应头
  • request,本次请求信息
  • isRedirect,是否重定向(Flutter Web不可用)
  • statusCode,状态码
  • statusMessage,状态信息
  • redirects,重定向信息(Flutter Web不可用)
  • extra,响应对象的自定义字段(可以在拦截器中设置它),调用方可以在 then 中获取.

拦截器

每个 Dio 实例都可以添加任意多个拦截器,他们组成一个队列,拦截器队列的执行顺序是 FIFO。通过拦截器你可以在请求之前或响应之后(但还没有被 thencatchError处理)做一些统一的预处理操作。

  void fetchAlbum() async {
    Dio dio = Dio();
    dio.interceptors.add(InterceptorsWrapper(
        onRequest: (RequestOptions options) {
          print("---------------onRequest------------------");
          return options;
        },
        onResponse: (Response e) {
          print("-------------onResponse-----------------");
          return e;
        },
        onError: (DioError  e) {
          print("--------------------onError-------------");
          return e;
        }));
    dio.interceptors.add(InterceptorsWrapper(
        onRequest: (RequestOptions options) {
          print("||||||||||||||||||||||||onRequest||||||||||||||||||||||||");
          return options;
        },
        onResponse: (Response e) {
          print("||||||||||||||||||||||||onResponse||||||||||||||||||||||||");
          return e;
        },
        onError: (DioError  e) {
          print("||||||||||||||||||||||||onError||||||||||||||||||||||||");
          return e;
        }));
      /// 这个请求因为是 http:///,所以会出错
    Response response = await dio.get("http:///xxx.xxx.xxx.xxx:8080/LifeKeeper/SelectBillByUserId",queryParameters: {
      "userId":"d969887a274347b98a0b99a830a2d6f4"
    });
    print(response.data.toString());
  }

上面的代码执行结果如下:

I/flutter (26652): ---------------onRequest------------------
I/flutter (26652): ||||||||||||||||||||||||onRequest||||||||||||||||||||||||
I/flutter (26652): --------------------onError-------------
I/flutter (26652): ||||||||||||||||||||||||onError||||||||||||||||||||||||
E/flutter (26652): [ERROR:flutter/lib/ui/ui_dart_state.cc(157)] Unhandled Exception: DioError [DioErrorType.DEFAULT]: Invalid argum......

我们设置了两个拦截器,从输出内容也可以看出来,执行顺序是 FIFO

锁定/解锁拦截器

你可以通过调用拦截器的 lock()/unlock 方法来锁定/解锁拦截器。一旦请求/响应拦截器被锁定,接下来的请求/响应将会在进入请求/响应拦截器之前排队等待,直到解锁后,这些入队的请求才会继续执行(进入拦截器)。这在一些需要串行化请求/响应的场景中非常实用,后面我们将给出一个示例。

tokenDio = Dio(); //Create a instance to request the token.
tokenDio.options = dio.options;
dio.interceptors.add(InterceptorsWrapper(
    onRequest:(Options options) async {
        // If no token, request token firstly and lock this interceptor
        // to prevent other request enter this interceptor.
        dio.interceptors.requestLock.lock();
        // We use a Dio(to avoid dead lock) instance to request token.
        Response response = await tokenDio.get("/token");
        //Set the token to headers
        options.headers["token"] = response.data["data"]["token"];
        dio.interceptors.requestLock.unlock();
        return options; //continue
    }
));

清空等待队列

之前说过,我们可以为 dio 设置多个拦截器,在执行过程中,可以调用拦截器的clear()方法来清空等待队列。

在拦截器中完成和终止请求/响应

在所有拦截器中,你都可以改变请求执行流, 如果你想完成请求/响应并返回自定义数据,你可以返回一个 Response 对象或返回 dio.resolve(data)的结果。 如果你想终止(触发一个错误,上层catchError会被调用)一个请求/响应,那么可以返回一个DioError 对象或返回 dio.reject(errMsg) 的结果.

dio.interceptors.add(InterceptorsWrapper(
  onRequest:(RequestOptions options){
   return dio.resolve("fake data")
  },
));
Response response = await dio.get("/test");
print(response.data);//"fake data"

results matching ""

    No results matching ""